// kernel.c begin
/**
 * =============================================================================
 * @file    kernel.c
 * @brief   RISC-V 裸机内核主文件
 * @author  (Your Name)
 * @date    2025-09-11
 *
 * @details
 *  - **功能**:
 *    1.  提供基础的串口 (UART) 驱动，用于字符和字符串输出。
 *    2.  实现一个精简版的 `printf`，支持 %s, %d, %x, %c 格式。
 *    3.  建立了一个由硬件触发的 CPU 异常处理机制 (Exception Handler)。
 *    4.  提供了一个由软件调用的主动 `Panic` 机制，用于断言和逻辑错误处理。
 *    5.  包含 `memset` 和 `delay` 等基础库函数。
 *  - **环境**:
 *    - 目标架构: RISC-V 32位 (RV32IMA)
 *    - 运行环境: QEMU 'virt' 虚拟开发板
 * =============================================================================
 */

// --- 1. 基础类型定义 ---
// 在裸机环境中，我们自己定义这些标准类型以确保可移植性和明确性。
typedef unsigned char uint8_t;
typedef unsigned int   uint32_t;
typedef uint32_t       size_t;

// --- 2. 可变参数宏定义 ---
// 我们无法包含标准库 <stdarg.h>，但可以利用GCC的内建函数来实现相同的功能。
// 这是实现自定义 printf 的关键。
typedef __builtin_va_list va_list; // 定义 va_list 类型，GCC内建
#define va_start(v, l) __builtin_va_start(v, l) // 初始化 va_list
#define va_end(v)      __builtin_va_end(v)      // 结束 va_list 处理
#define va_arg(v, l)   __builtin_va_arg(v, l)   // 获取下一个参数

// --- 3. 硬件与内存映射 I/O 定义 ---
/**
 * @brief 内存映射I/O (Memory-Mapped I/O) 宏
 * @details
 *  - `volatile` 关键字至关重要，它告诉编译器不要对这些内存访问进行优化
 *    （例如缓存到寄存器），因为每次读写都可能直接与硬件状态交互。
 */
#define IOB(addr) (*(volatile uint8_t *)(addr))
#define IOW(addr) (*(volatile uint32_t *)(addr))

// QEMU 'virt' 虚拟开发板上的 UART (通用异步收发传输器) 寄存器地址
#define UART 0x10000000
#define UART_THR (UART + 0)   // 发送保持寄存器 (Transmitter Holding Register)
#define UART_LSR (UART + 5)   // 线路状态寄存器 (Line Status Register)
#define UART_LSR_TX_EMPTY 0x40    // 发送器空闲标志位 (第6位)

#define CSRR(reg, val) do { \
    __asm__ volatile ("csrr %0, " #reg : "=r"(val) :: "memory"); \
} while (0)

#define CSRW(reg, val) do { \
    __asm__ volatile ("csrw " #reg ", %0" :: "r"(val) : "memory"); \
} while (0)

// --- 4. 前置声明 ---
// 提前声明 IO 函数，以便 panic 函数可以使用它们。
int lib_printf(char *fmt, ...);

// --- 5. 主动 Panic 机制 ---
/**
 * @brief 内核 Panic 函数 (软件触发)
 * @param file 发生 Panic 的文件名 (由 __FILE__ 宏提供)
 * @param line 发生 Panic 的行号 (由 __LINE__ 宏提供)
 * @param msg  自定义的 Panic 信息
 * @details
 *  - 此函数用于软件断言失败或遇到无法恢复的逻辑错误时主动调用。
 *  - 它会打印详细的错误位置和信息，然后使系统停机。
 *  - `__attribute__((noreturn))` 是一个GCC扩展，它告诉编译器这个函数不会返回，
 *    有助于生成更优化的代码，并能让编译器在函数返回后发现死代码时给出警告。
 */
void panic(const char *file, int line, const char *msg) __attribute__((noreturn));
void panic(const char *file, int line, const char *msg) {
    // 打印Panic头部信息
    lib_printf("\n\n!!! KERNEL PANIC (Assertion Failed) !!!\n");
    lib_printf("=======================================\n");
    // 使用预处理器宏提供的文件名和行号，精确定位问题
    lib_printf("Location: %s:%d\n", file, line);
    // 打印程序员自定义的错误消息
    lib_printf("Message : %s\n", msg);
    lib_printf("=======================================\n");

    // 进入无限循环，使系统停机 (Halt)
    while (1);
}

/**
 * @brief 主动触发 Panic 的宏 (使用 do-while(0) 技巧)
 * @details
 *  - **使用方法**: 在代码中直接调用 `PANIC("some error message");`。
 *  - **工作原理**: 预处理器会自动将当前的 文件名(__FILE__) 和 行号(__LINE__) 
 *    作为参数传递给 `panic` 函数。
 *  - **do-while(0) 优势**: 这个结构确保宏在任何语法环境中都能表现得像一个
 *    标准的、完整的C语句，从而避免在 `if-else` 等无花括号的结构中出现语法错误。
 */
#define PANIC(msg) \
    do { \
        panic(__FILE__, __LINE__, msg); \
    } while (0)


// --- 6. 基础 I/O 库函数 ---

/**
 * @brief 通过 UART 输出单个字符
 * @param c 要输出的字符
 * @return 输出的字符
 */
int libputc(char c) {
    // 这是一个 "忙等待" (busy-wait) 循环。它会持续检查线路状态寄存器(LSR)的
    // 第6位，直到该位为1，表示发送缓冲区为空，可以发送下一个字符。
    while ((IOB(UART_LSR) & UART_LSR_TX_EMPTY) == 0);
    
    // 将字符写入发送保持寄存器(THR)，硬件会自动将其发送出去。
    IOB(UART_THR) = c;
    return c;
}

/**
 * @brief 输出一个以 null ('\0') 结尾的字符串
 * @param s 字符串指针
 * @return 0
 */
int libputs(char *s) {
    while (*s) {
        libputc(*s++);
    }
    return 0;
}

/**
 * @brief 以十进制形式输出一个整数
 * @param d 整数
 * @return 0
 */
int libputd(int d) {
    // 处理负数
    if (d < 0) {
        libputc('-');
        // 特殊处理 int 类型的最小值，直接取反会溢出
        if (d == -2147483648) {
            libputs("2147483648");
            return 0;
        }
        d = -d;
    }
    // 使用递归先输出高位
    if (d >= 10) {
        libputd(d / 10);
    }
    // 输出当前最低位
    libputc('0' + (d % 10));
    return 0;
}

/**
 * @brief 以十六进制形式输出一个32位无符号整数
 * @param x 32位无符号整数
 */
void lib_puth(uint32_t x) {
    libputs("0x");
    for (int i = 7; i >= 0; i--) {
        // 每次提取4位 (一个半字节, nibble)
        int nibble = (x >> (i * 4)) & 0xf;
        // 将0-15的数值映射到 '0'-'9', 'A'-'F' 字符并输出
        libputc("0123456789ABCDEF"[nibble]);
    }
}

/**
 * @brief 精简版的格式化输出函数
 * @param fmt 格式字符串
 * @param ... 可变参数
 */
int lib_printf(char *fmt, ...) {
    va_list ap; // 定义一个指向可变参数列表的指针
    va_start(ap, fmt); // 初始化ap，使其指向fmt之后的第一个可变参数

    while (*fmt) {
        if (*fmt == '%') {
            fmt++; // 跳过 '%'
            switch (*fmt) {
                case 's': libputs(va_arg(ap, char *)); break;
                case 'd': libputd(va_arg(ap, int)); break;
                case 'x': lib_puth(va_arg(ap, uint32_t)); break;
                // 注意：char类型的参数在传递时会被提升为int类型
                case 'c': libputc((char)va_arg(ap, int)); break;
                case '%': libputc('%'); break;
                default: libputc('%'); libputc(*fmt); break;
            }
            fmt++;
        } else {
            libputc(*fmt);
            fmt++;
        }
    }
    va_end(ap); // 清理工作
    return 0;
}


// --- 7. CPU 异常处理 (硬件触发) ---

/**
 * @brief 读取 CSR (控制与状态寄存器) 的内联汇编函数
 * @details
 *  - `__asm__ __volatile__` 是GCC的内联汇编语法。
 *  - `csrr %0, mepc` 是一条RISC-V指令，意思是 "读取 mepc 寄存器的值到 %0 操作数"。
 *  - `"=r"(mepc)` 定义了约束：
 *    - `%0` 对应C变量 `mepc`。
 *    - `"=r"` 表示这是一个输出 (`=`)，且可以使用任何通用寄存器 (`r`)。
 */
static inline uint32_t read_mepc() {
    uint32_t mepc;
    __asm__ __volatile__("csrr %0, mepc" : "=r"(mepc));
    return mepc;
}

static inline uint32_t read_mcause() {
    uint32_t mcause;
    __asm__ __volatile__("csrr %0, mcause" : "=r"(mcause));
    return mcause;
}

static inline uint32_t read_mtval() {
    uint32_t mtval;
    __asm__ __volatile__("csrr %0, mtval" : "=r"(mtval));
    return mtval;
}

/**
 * @brief C语言实现的异常处理核心函数
 * @details
 *  - 此函数由 `start.s` 中的汇编代码 `exception_handler` 调用。
 *  - 它的任务是诊断并报告由硬件触发的CPU异常，然后停机。
 */
void c_exception_handler() {
    uint32_t exception_pc;        // 导致异常的指令地址
    uint32_t exception_cause;     // 异常原因代码
    uint32_t exception_value;     // 异常相关的值（如非法地址或指令）

    // 使用 CSRR 宏读取 mepc 寄存器
    CSRR(mepc, exception_pc);
    
    // 使用原有的 read_* 函数读取其他寄存器
    exception_cause = read_mcause();
    exception_value = read_mtval();

    // 打印硬件异常的Panic信息
    lib_printf("\n\n!!! KERNEL PANIC (CPU Exception) !!!\n");
    lib_printf("====================================\n");
    lib_printf("Exception Cause : 0x%x\n", exception_cause);
    lib_printf("Exception PC    : 0x%x\n", exception_pc);
    lib_printf("Exception Value : 0x%x\n", exception_value);
    lib_printf("====================================\n");

    // 解析异常原因
    lib_printf("Exception Description: ");
    
    // RISC-V 异常原因代码 (mcause) 解析
    // 最高位为中断标志位 (1=中断, 0=异常)
    uint32_t is_interrupt = exception_cause & 0x80000000;
    uint32_t exception_code = exception_cause & 0x7FFFFFFF;
    
    if (is_interrupt) {
        lib_printf("Interrupt - ");
        switch (exception_code) {
            case 0: lib_printf("User software interrupt\n"); break;
            case 1: lib_printf("Supervisor software interrupt\n"); break;
            case 2: lib_printf("Reserved for future use\n"); break;
            case 3: lib_printf("Machine software interrupt\n"); break;
            case 4: lib_printf("User timer interrupt\n"); break;
            case 5: lib_printf("Supervisor timer interrupt\n"); break;
            case 6: lib_printf("Reserved for future use\n"); break;
            case 7: lib_printf("Machine timer interrupt\n"); break;
            case 8: lib_printf("User external interrupt\n"); break;
            case 9: lib_printf("Supervisor external interrupt\n"); break;
            case 10: lib_printf("Reserved for future use\n"); break;
            case 11: lib_printf("Machine external interrupt\n"); break;
            default: lib_printf("Unknown interrupt (%d)\n", exception_code); break;
        }
    } else {
        lib_printf("Synchronous Exception - ");
        switch (exception_code) {
            case 0: lib_printf("Instruction address misaligned\n"); break;
            case 1: lib_printf("Instruction access fault\n"); break;
            case 2: lib_printf("Illegal instruction\n"); break;
            case 3: lib_printf("Breakpoint\n"); break;
            case 4: lib_printf("Load address misaligned\n"); break;
            case 5: lib_printf("Load access fault\n"); break;
            case 6: lib_printf("Store/AMO address misaligned\n"); break;
            case 7: lib_printf("Store/AMO access fault\n"); break;
            case 8: lib_printf("Environment call from U-mode\n"); break;
            case 9: lib_printf("Environment call from S-mode\n"); break;
            case 11: lib_printf("Environment call from M-mode\n"); break;
            case 12: lib_printf("Instruction page fault\n"); break;
            case 13: lib_printf("Load page fault\n"); break;
            case 15: lib_printf("Store/AMO page fault\n"); break;
            default: lib_printf("Unknown exception (%d)\n", exception_code); break;
        }
        
        // 根据异常类型提供更多详细信息
        switch (exception_code) {
            case 2: // Illegal instruction
                lib_printf("Faulting instruction value: 0x%x\n", exception_value);
                break;
            case 0: // Instruction address misaligned
            case 1: // Instruction access fault
            case 4: // Load address misaligned
            case 5: // Load access fault
            case 6: // Store/AMO address misaligned
            case 7: // Store/AMO access fault
            case 12: // Instruction page fault
            case 13: // Load page fault
            case 15: // Store/AMO page fault
                lib_printf("Faulting address: 0x%x\n", exception_value);
                break;
        }
    }

    // 打印可能的解决方案或调试建议
    lib_printf("\nDebug Suggestions:\n");
    lib_printf("1. Check if the instruction at 0x%x is valid\n", exception_pc);
    lib_printf("2. Verify memory access permissions\n");
    lib_printf("3. Ensure stack pointers are properly initialized\n");
    lib_printf("4. Check for buffer overflows or other memory corruption\n");

    // 进入无限循环，使系统停机 (Halt)
    lib_printf("\nSystem halted. Reset to continue.\n");
    // while (1);
    PANIC("NONE WORK AT INFINITE LOOP");
}

// --- 8. 其它工具函数 ---

/**
 * @brief 简单的软件延时函数
 * @param n 延时循环次数的倍数
 */
void lib_delay(int n) {
    // 使用 volatile 防止编译器优化掉这个循环
    volatile int count = n * 1000;
    while (count--);
}

/**
 * @brief 实现标准库的 memset 函数
 * @param buf 内存区域指针
 * @param c 要设置的字节值
 * @param n 要设置的字节数
 * @return 内存区域指针
 */
void *memset(void *buf, int c, size_t n) {
    uint8_t *p = (uint8_t *)buf;
    while (n--) {
        *p++ = c;
    }
    return buf;
}

// --- 9. 内核主逻辑 ---

// 从链接脚本 kernel.ld 中导入BSS段的起始和结束地址
extern char _bss_start[], _bss_end[];

/**
 * @brief 用于测试硬件异常处理的函数
 * @details 它会故意执行一个非法操作来触发CPU异常
 */
void trigger_cpu_exception() {
    // 尝试向地址 0x0 写入数据。这是一个受保护的地址，
    // 必然会触发一个 "Store/AMO access fault" (mcause=7) 异常。
    int *p = (int*)0x0;
    *p = 123;
}

/**
 * @brief 用于测试主动Panic的函数
 */
void test_assertion(int *ptr) {
    lib_printf("Testing assertion with a pointer...\n");
    if (ptr == (void*)0) {
        // 检查到指针为空，这是一个严重的逻辑错误，主动触发 Panic
        PANIC("Null pointer detected!");
    }
    // 如果指针不为空，则正常执行
    lib_printf("Pointer is valid.\n");
}


/**
 * @brief 内核主函数 (C语言入口)
 * @details 此函数由 `start.s` 中的 `_start` 跳转而来
 */
void kernel_main(void) {
    // 步骤1: 清空BSS段。
    // BSS段存放的是未初始化的全局变量和静态变量，C语言标准要求它们在
    // main 函数执行前被初始化为0。在裸机环境中，我们需要手动完成。
    memset(_bss_start, 0, _bss_end - _bss_start);
    
    // 步骤2: 初始化串口并打印启动信息
    libputs("=== RISC-V Bare Metal Kernel ===\n");
    libputs("BSS section cleared.\n\n");
    trigger_cpu_exception();
    // 步骤2: 演示各种输出功能
    lib_printf("String: %s\n", "Hello, World!");
    lib_printf("Decimal: %d\n", 12345);
    lib_printf("Negative: %d\n", -6789);
    lib_printf("Hexadecimal: %x\n", 0xDEADBEEF);
    lib_printf("Character: %c%c%c\n", 'A', 'B', 'C');
    lib_printf("Mixed: %s %d 0x%x\n", "Test", 42, 0xABCD);
    
    // 步骤3: 测试主动 Panic 机制
    int valid_var = 10;
    test_assertion(&valid_var); // 第一次测试：传递有效指针，应该会通过
    test_assertion((void*)0);    // 第二次测试：传递空指针，应该会触发 Panic

    // 如果上面的 test_assertion 触发了 Panic，下面的代码将永远不会执行
    
    // 步骤4: (可选) 测试硬件异常
    // 如果你想测试硬件异常，可以取消下面这行的注释
    // trigger_cpu_exception();
    
    lib_printf("This line should never be printed.\n");
    int counter = 0;
    while (1) {
        lib_printf("Counter: %d\n", counter++);
        lib_delay(500);
    }
}
// kernel.c end